package com.untrance.shop.core.builder.query;

import java.util.AbstractCollection;
import java.util.HashMap;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import com.google.appengine.repackaged.com.google.common.base.Preconditions;
import com.untrance.shop.common.enums.OrderDirectionType;
import com.untrance.shop.common.filters.model.Filter;

/**
 * @author eugene zadyra
 */
public abstract class AbstractQueryBuilder<F extends Filter> {
	private Map<String, Object> filterValues;
	protected F filter;

	protected EntityManager em;

	protected abstract String getEntityName();

	protected abstract String getOrderField();

	protected abstract void addFilters();

	protected abstract OrderDirectionType getDefaultOrderDirection();

	public void addFilterValue(String name, Object value) {
		if (value == null)
			return;
		if (filterValues == null) {
			filterValues = new HashMap<String, Object>();
		}
		filterValues.put(name, value);
	}

	private String prepareFilters() {
		if (filterValues != null) {
			StringBuilder sb = new StringBuilder();
			boolean first = true;
			for (String key : filterValues.keySet()) {
				Object value = filterValues.get(key);
				sb.append(first ? "" : " AND ");
				if (value instanceof AbstractCollection) {
					sb.append(":").append(key).append(".contains(").append(key).append(")");
				} else {
					sb.append(key).append("=:").append(key);
				}
				first = false;
			}
			return sb.toString();
		}
		return null;
	}

	private Query appendParams(Query query) {
		if (filterValues != null) {
			for (String key : filterValues.keySet()) {
				Object value = filterValues.get(key);
				query.setParameter(key, value);
			}
		}
		return query;
	}

	private String getOrderDirection() {
		if (getDefaultOrderDirection() != null) {
			return getDefaultOrderDirection().name();
		} else if (filter.getOrderDirection() != null) {
			return filter.getOrderDirection().name();
		} else {
			return OrderDirectionType.ASC.name();
		}
	}

	private Query prepareQuery(boolean calculateCount) {
		Query query = em.createQuery(prepareQueryString(calculateCount));
		if (filterValues != null) {
			query = appendParams(query);
		}
		return query;
	}

	public String prepareQueryString(boolean calculateCount) {
		StringBuilder queryString = new StringBuilder();
		queryString.append("SELECT ");
		if (calculateCount) {
			queryString.append("COUNT(");
		}
		queryString.append("e");
		if (filter.getFieldToSelect() != null) {
			queryString.append(".").append(filter.getFieldToSelect());
		}
		if (calculateCount) {
			queryString.append(")");
		}
		queryString.append(" FROM ").append(getEntityName()).append(" e");
		if (filterValues != null) {
			queryString.append(" WHERE ").append(prepareFilters());
		}
		if (getOrderField() != null) {
			queryString.append(" ORDER BY ").append(getOrderField()).append(" ").append(getOrderDirection());
		}
		return queryString.toString();
	}

	public Query buildQuery() {
		Preconditions.checkNotNull(filter, "Filter can not be NULL");
		return prepareQuery(false);
	}

	public Query buildCountQuery() {
		Preconditions.checkNotNull(filter, "Filter can not be NULL");
		return prepareQuery(true);
	}

}
